package nl.unimaas.micc.umsnooker.control.ui.controller;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

import javax.swing.plaf.basic.BasicDesktopIconUI.MouseInputHandler;

import nl.unimaas.micc.umsnooker.domain.BilliardBall;
import nl.unimaas.micc.umsnooker.gameflow.GameState;
import nl.unimaas.micc.umsnooker.gameflow.phases.AimingPhase;
import nl.unimaas.micc.umsnooker.presentation.TablePresentation;
import nl.unimaas.micc.umsnooker.simulation.EventHandler;
import nl.unimaas.micc.umsnooker.simulation.InTimeSimulation;
import nl.unimaas.micc.umsnooker.simulation.Simulation;
import nl.unimaas.micc.umsnooker.simulation.event.BallCollision;
import nl.unimaas.micc.umsnooker.simulation.event.CueInteraction;
import nl.unimaas.micc.umsnooker.simulation.event.Event;
import nl.unimaas.micc.umsnooker.simulation.event.PocketingEvent;

import com.jme.input.ChaseCamera;
import com.jme.input.InputHandler;
import com.jme.input.MouseInput;
import com.jme.input.action.InputActionEvent;
import com.jme.input.action.MouseInputAction;
import com.jme.math.FastMath;
import com.jme.math.Vector3f;

public class AimingControl extends InputHandler
{
	
	
	public AimingControl(final GameState state, final ChaseCamera camControl,
			final TablePresentation presentation, final AimingPhase phase)
	{
		for (BilliardBall ball : state.getBalls())
		{
			presentation.updateBall(ball);
		}
		presentation.showShotVisualization(true);

		
		// final Node object = presentation.getCueBallNode();

		class AimingMouseInput extends MouseInputAction
		{
			class AimingHandler implements EventHandler
			{
				public int collisionCount = 0;
				public BilliardBall monitoredBall;
				public ArrayList<ArrayList<Vector3f>> linePointsList;
				public ArrayList<Vector3f> points;

				
				@Override
				public void handle(Event e)
				{
					if (e instanceof BallCollision)
					{
						Set<BilliardBall> balls = e.getBallKeys();
						if (balls.contains(monitoredBall))
						{
							sim.getBall(monitoredBall);
							addPointFromMonitoredBall();
							BilliardBall newMon = monitoredBall;
							for (BilliardBall b : balls)
							{
								if (!b.equals(monitoredBall))
								{
									newMon = (BilliardBall) b.clone();
								}
							}
							monitoredBall=newMon;

							linePointsList.add(points);
							presentation.updateShotVisualization(linePointsList);
							points = new ArrayList<Vector3f>();
							addPointFromMonitoredBall();
							
							collisionCount++;
							if (collisionCount > visualizationDepth)
							{
								sim.pause();
							}
						}
					}
					if (e instanceof PocketingEvent)
					{
						Vector3f newPoint = ((PocketingEvent)e).getPocket().getPosition().clone();
						addPoint(newPoint);
						linePointsList.add(points);
						sim.pause();
					}
				}
				
				public void addPointFromMonitoredBall()
				{
					addPoint(monitoredBall.getPosition().clone());
				}
				
				public void addPoint(Vector3f point)
				{
					point.z = monitoredBall.getRadius();
					point.x = point.x	- (0.5f * state.getTable().getWidth());
					point.y = point.y - (0.5f * state.getTable().getLength());
					points.add(point);
				}
			}


			
			private final float maxV = 4.5f;

			private float angX = 0;
			private float angY = 0;

			private float transX = 0, transY = 0;
			private float velocity = 0;

			private long lastUpdate = System.currentTimeMillis();
			private long updateFrequency = 50;

			private float accuracy = 0.0005f;
			private int visualizationDepth = 2;

			private boolean goNext = false;
			private boolean finished = false;
			private Simulation sim;
			private AimingHandler aimingHandler;

			public AimingMouseInput()
			{
				sim = new InTimeSimulation(	new HashSet<BilliardBall>(state.getBalls()),state.getTable());
				aimingHandler = new AimingHandler();
				sim.addEventHandler(aimingHandler);

			}
			@Override
			public void performAction(InputActionEvent evt)
			{
				if (Math.abs(MouseInput.get().getWheelDelta()) > 0)
				{
					float increase = MouseInput.get().getWheelDelta() / (120);
					if (velocity - (0.025f * increase) >= 0
							&& velocity - (0.025f * increase) <= maxV)
					{
						velocity -= (0.025f * increase);
					}

				}
				if (MouseInput.get().isButtonDown(0))
				{

					goNext = true;
					// float transY = MouseInput.get().getYDelta() * 0.005f;
					// cue.getLocalTranslation().addLocal(0, 0, transY);
				}
				else if (goNext)
				{
					presentation.removeCue();
					phase.setAngDest(angX);
					phase.setAngElev(angY);
					phase.setTransX(transX);
					phase.setTransY(transY);

					phase.setVelocity(velocity);
					finished = true;

					phase.finish();
					goNext = false;
				}
				else
				{

					float x = MouseInput.get().getXDelta() * 0.0005f;
					float y = MouseInput.get().getYDelta() * 0.0005f;

					if (MouseInput.get().isButtonDown(1))
					{
						camControl.setTargetOffset(new Vector3f(0, 0, .3f));
						camControl.setMaxDistance(1.5f);
						camControl.setMinDistance(1f);

						float viableRadius = state.getCueBall().getRadius() * .9f;

						if (FastMath.sqr(transX + x) + FastMath.sqr(transY + y) > FastMath
								.sqr(viableRadius))
						{
							float angle = FastMath
									.atan2(transY + y, transX + x);
							transX = viableRadius * FastMath.cos(angle);
							transY = viableRadius * FastMath.sin(angle);
						}
						else
						{
							transX += x;
							transY += y;
						}
						presentation.transposeCue(transX, transY);

					}
					else if (MouseInput.get().isButtonDown(2))
					{
						if (velocity - (y * 0.5) >= 0
								&& velocity - (y * 0.5) <= maxV)
						{
							velocity -= (y * 0.5);

						}

					}
					else
					{
						camControl.setMaxDistance(3);
						camControl.setMinDistance(1);
						angX += x;
						if (y + angY > 0 && y + angY < FastMath.PI / 2)
						{
							angY += y;
						}
						presentation.rotateCue(angX, angY);

					}
					presentation.setCueDistance(velocity / 10);
				}
				if (!finished)
				{
					updatePathVisualization();
				}

			}

			private void updatePathVisualization()
			{

				long time = System.currentTimeMillis();
				if ((time - lastUpdate) > updateFrequency)
				{
					if (velocity > 0)
					{

						BilliardBall simCueBall = (BilliardBall) state.getCueBall();
						
						Event cueShot = new CueInteraction(0, simCueBall, angX, angY, transX, transY, velocity);

						Set<Event> initEvents = new HashSet<Event>();
						initEvents.add(cueShot);
						
						aimingHandler.collisionCount = 0;
						aimingHandler.monitoredBall = (BilliardBall) simCueBall.clone();
						aimingHandler.linePointsList = new ArrayList<ArrayList<Vector3f>>();
						aimingHandler.points = new ArrayList<Vector3f>();
						aimingHandler.addPointFromMonitoredBall();

						sim.init(initEvents);



						int step = 1;
						while (!sim.isPaused() && sim.hasNextEvent())
						{
							sim.getBall(aimingHandler.monitoredBall);
							aimingHandler.addPointFromMonitoredBall();
							sim.advanceTime(accuracy*step);
							step++;
						}
						aimingHandler.linePointsList.add(aimingHandler.points);
						presentation.updateShotVisualization(aimingHandler.linePointsList);

						//sim.reset();

					}
					lastUpdate = System.currentTimeMillis();
				}

			}
		}
		this.addAction(new AimingMouseInput());
	}

}
